<!-- @format -->

<template>
  <div ref="contentContainer" class="markdown-math-container">
    <slot v-if="!content"></slot>
    <div v-else v-html="renderedContent"></div>
  </div>
</template>

<script lang="ts">
import { defineComponent, onMounted, onUpdated, ref, computed, watch } from 'vue'
import MarkdownIt from 'markdown-it'
import DOMPurify from 'dompurify'

declare global {
  interface Window {
    MathJax: any;
  }
}

export default defineComponent({
  name: 'MarkdownMathRenderer',
  props: {
    content: {
      type: String,
      default: ''
    },
    options: {
      type: Object,
      default: () => ({})
    }
  },
  setup(props) {
    const contentContainer = ref<HTMLElement | null>(null)
    
    // 创建markdown-it实例
    const md = new MarkdownIt({
      html: true,
      breaks: true,
      linkify: true,
      typographer: true
    })
    
    // 自定义标记器处理行内公式 $...$
    md.inline.ruler.before('escape', 'inline_math', (state, silent) => {
      const start = state.pos
      if (state.src[start] !== '$') return false

      // 查找结束的 $
      let end = start + 1
      while (end < state.src.length && state.src[end] !== '$') {
        end++
      }

      // 没有找到结束的 $ 或者内容为空
      if (end === state.src.length || end === start + 1) return false

      // 不处理转义的 $
      if (start > 0 && state.src[start - 1] === '\\') return false

      // 检查前后是否有字母或数字（避免误识别货币符号等）
      if (
        (start > 0 && /[\w]/.test(state.src[start - 1])) ||
        (end < state.src.length - 1 && /[\w]/.test(state.src[end + 1]))
      ) {
        return false
      }

      if (!silent) {
        const token = state.push('inline_math', '', 0)
        // 预处理公式内容，处理常见的问题
        let content = state.src.slice(start + 1, end)
        // 增强预处理 - 处理更多情况
        content = content
          // 修复常见的上下标问题
          .replace(/([_^])([a-zA-Z0-9]+)/g, '$1{$2}')
          // 处理分数
          .replace(/\\frac([^{])([^{])/g, '\\frac{$1}{$2}')
          .replace(/\\frac{([^}]+)}([^{])/g, '\\frac{$1}{$2}')
          // 处理特殊情况：二次方程根式
          .replace(/\\sqrt{b\^2-4ac}/g, '\\sqrt{b^{2}-4ac}')
          // 处理特殊符号
          .replace(/(\\pm|\\mp|\\times|\\div|\\cdot)([^\s{])/g, '$1 $2')
        token.content = content
        token.markup = '$'
      }

      state.pos = end + 1
      return true
    })

    // 自定义标记器处理块级公式 $$...$$
    md.block.ruler.before(
      'fence',
      'block_math',
      (state, startLine, endLine, silent) => {
        const start = state.bMarks[startLine] + state.tShift[startLine]
        const max = state.eMarks[startLine]

        // 检查开始标记 $$
        if (
          max - start < 2 ||
          state.src[start] !== '$' ||
          state.src[start + 1] !== '$'
        ) {
          return false
        }

        let line = startLine
        let found = false

        // 查找结束标记 $$
        while (line < endLine) {
          line++
          if (line >= endLine) break

          const start = state.bMarks[line] + state.tShift[line]
          const max = state.eMarks[line]
          if (start < max && state.src.slice(start, max).indexOf('$$') >= 0) {
            found = true
            break
          }
        }

        if (!found) return false

        // 不处理silent模式
        if (silent) return true

        let content = state
          .getLines(startLine, line, state.tShift[startLine], true)
          .replace(/^\$\$/, '')
          .replace(/\$\$$/, '')
          .trim()

        // 预处理块级公式内容，修复常见问题
        content = content
          // 基本的上下标处理
          .replace(/([_^])([a-zA-Z0-9]+)/g, '$1{$2}')
          // 确保sum和int等符号正确使用limits
          .replace(/(\\sum|\\int|\\prod)_(\S)/g, '$1_{$2}')
          .replace(/(\\sum|\\int|\\prod)\^(\S)/g, '$1^{$2}')
          // 处理平方根和根号
          .replace(/\\sqrt([^{])/g, '\\sqrt{$1}')
          // 处理特殊符号前后的空格
          .replace(/(\\pm|\\mp|\\times|\\div|\\cdot)([^\s{])/g, '$1 $2')
          // 确保分数正确格式化
          .replace(/\\frac(\S)(\S)/g, '\\frac{$1}{$2}')
          .replace(/\\frac{([^}]+)}([^{])/g, '\\frac{$1}{$2}')
          .replace(/\\frac([^{])\{([^}]+)\}/g, '\\frac{$1}{$2}')
          // 处理复杂分数表达式
          .replace(/\\frac{([^{}]+)}{([^{}]+)}/g, (match, p1, p2) => {
            // 确保分子分母中的特殊字符和上下标被正确处理
            const processedP1 = p1
              .replace(/(\\pm|\\mp|\\times|\\div|\\cdot)/g, '{$1}')
              .replace(/([_^])([^{])/g, '$1{$2}')
            const processedP2 = p2
              .replace(/(\\pm|\\mp|\\times|\\div|\\cdot)/g, '{$1}')
              .replace(/([_^])([^{])/g, '$1{$2}')
            return `\\frac{${processedP1}}{${processedP2}}`
          })
          // 处理下标中的特殊情况
          .replace(/_\{([^}]*)\}/g, (match) => match.replace(/,/g, '{,}'))
          // 处理上标中的特殊情况
          .replace(/\^\{([^}]*)\}/g, (match) => match.replace(/,/g, '{,}'))
          // 处理特殊情况：二次方程根式
          .replace(/\\sqrt{b\^2-4ac}/g, '\\sqrt{b^{2}-4ac}')
          // 处理特殊情况：带有\pm的表达式
          .replace(/(\\pm|\\mp)\s*\\sqrt/g, '{$1}\\sqrt')

        const token = state.push('block_math', '', 0)
        token.content = content
        token.markup = '$$'
        token.map = [startLine, line + 1]

        state.line = line + 1
        return true
      },
    )

    // 禁用markdown-it中可能干扰数学公式的规则
    md.disable(['backticks'])
    
    // 配置MathJax
    const configureMathJax = () => {
      window.MathJax = {
        tex: {
          inlineMath: [['\\(', '\\)'], ['$', '$']],
          displayMath: [['$$', '$$']],
          packages: ['base', 'ams', 'noerrors', 'noundefined', 'newcommand', 'boldsymbol'],
          processEscapes: true,  // 允许使用\$来表示文本中的$符号
          processEnvironments: true  // 处理LaTeX环境
        },
        options: {
          enableMenu: false,
          ...props.options,
          renderActions: {
            addMenu: [0, '', ''],
            checkLoading: [1000, () => '', '']
          }
        },
        startup: {
          pageReady: () => {
            return window.MathJax.startup.defaultPageReady()
          }
        }
      }
    }

    // 渲染MathJax
    const renderMathJax = () => {
      if (window.MathJax && window.MathJax.typesetPromise) {
        window.MathJax.typesetPromise([contentContainer.value])
          .catch((err: any) => {
            console.error('MathJax渲染错误:', err)
          })
      }
    }

    // 加载MathJax
    const loadMathJax = () => {
      if (!window.MathJax) {
        const script = document.createElement('script')
        script.src = 'https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js'
        script.async = true
        document.head.appendChild(script)
        
        script.onload = () => {
          configureMathJax()
          renderMathJax()
        }
      } else {
        configureMathJax()
        renderMathJax()
      }
    }

    // 处理内容
    const processContent = (content: string) => {
      if (!content) return ''
      
      // 保护数学公式，防止被markdown解析
      const mathExpressions: string[] = []
      
      // 替换块级公式 - 支持$$...$$格式
      let protectedContent = content.replace(/\$\$(.*?)\$\$/gs, (match) => {
        mathExpressions.push(match)
        return `%%MATH_BLOCK_${mathExpressions.length - 1}%%`
      })
      
      // 替换块级公式 - 支持\[...\]格式
      protectedContent = protectedContent.replace(/\\\[(.*?)\\\]/gs, (match) => {
        mathExpressions.push(match)
        return `%%MATH_BLOCK_${mathExpressions.length - 1}%%`
      })
      
      // 替换行内公式 - 支持\(\)格式
      protectedContent = protectedContent.replace(/\\\((.*?)\\\)/g, (match) => {
        mathExpressions.push(match)
        return `%%MATH_INLINE_${mathExpressions.length - 1}%%`
      })
      
      // 替换行内公式 - 支持$...$格式
      protectedContent = protectedContent.replace(/\$(.*?)\$/g, (match) => {
        // 排除转义的$符号
        if (match.match(/\\\$/)) {
          return match;
        }
        mathExpressions.push(match)
        return `%%MATH_INLINE_${mathExpressions.length - 1}%%`
      })
      
      // 渲染Markdown
      let htmlContent = md.render(protectedContent)
      
      // 恢复数学公式
      mathExpressions.forEach((expr, index) => {
        const blockPlaceholder = `%%MATH_BLOCK_${index}%%`;
        const inlinePlaceholder = `%%MATH_INLINE_${index}%%`;
        
        if (htmlContent.includes(blockPlaceholder)) {
          htmlContent = htmlContent.replace(blockPlaceholder, expr);
        }
        
        if (htmlContent.includes(inlinePlaceholder)) {
          htmlContent = htmlContent.replace(inlinePlaceholder, expr);
        }
      })
      
      // 使用DOMPurify清理HTML
      return DOMPurify.sanitize(htmlContent)
    }
    
    // 计算渲染后的内容
    const renderedContent = computed(() => {
      if (!props.content) return ''
      return processContent(props.content)
    })

    onMounted(() => {
      loadMathJax()
      // 如果使用slot，需要在挂载后渲染MathJax
      if (!props.content && contentContainer.value) {
        renderMathJax()
      }
    })

    onUpdated(() => {
      renderMathJax()
    })

    // 监听内容变化，重新渲染
    watch(() => props.content, () => {
      if (props.content) {
        // 内容通过props传入时，会通过v-html自动更新
        // 需要在内容更新后重新渲染MathJax
        setTimeout(() => {
          renderMathJax()
        }, 0)
      }
    })

    return {
      contentContainer,
      renderedContent
    }
  }
})
</script>

<style scoped>
.markdown-math-container {
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu,
    Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
  line-height: 1.6;
  color: #333;
  max-width: 100%;
  overflow-x: auto;
}

/* 样式继承自MarkdownRenderer */
:deep(h1),
:deep(h2),
:deep(h3),
:deep(h4),
:deep(h5),
:deep(h6) {
  margin-top: 1.5em;
  margin-bottom: 0.5em;
  font-weight: 600;
}

:deep(p) {
  margin: 1em 0;
}

:deep(code) {
  background-color: #f5f5f5;
  padding: 0.2em 0.4em;
  border-radius: 3px;
  font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace;
}

:deep(pre) {
  background-color: #f5f5f5;
  padding: 1em;
  border-radius: 5px;
  overflow-x: auto;
}

:deep(blockquote) {
  border-left: 4px solid #ddd;
  padding-left: 1em;
  color: #666;
  margin: 1em 0;
}

:deep(.formula-display) {
  margin-top: 30px;
  padding: 15px;
  background-color: #f0f8ff;
  border-radius: 6px;
  text-align: center;
}

:deep(.formula) {
  font-size: 1.2em;
  padding: 10px;
}
</style>